
#include "type.h"

static bool_t is_hex(char c)
{
	if(((c >= '0')&&(c <= '9'))
	||((c >= 'a')&&(c <= 'f'))
	||((c >= 'A')&&(c <= 'F'))){
		return 1;
	}
	else{
		return 0;
	}
}

static uint8_t char_to_uint(char c)
{
    uint8_t value;

    if (c >= '0' && c <= '9') {
        value = c - '0';
    } else if (c >= 'a' &&c <= 'f') {
        value = c - 'a' + 10;
    } else if (c >= 'A' && c<= 'F') {
        value = c - 'A' + 10;
    } else {
//        printf("Invalid hex character: %c\n", *s);
        value = 0;
    }

    return value;
}

static bool_t string_to_byte(uint8_t* byte, char* s)
{
//    printf("string %c%c\r\n", *s, *(s+1));
	*byte = 0;
	if(is_hex(*s)&&(is_hex(*(s+1)))){
		*byte = char_to_uint(*s);
		*byte <<= 4;
		*byte += char_to_uint(*(s+1));
//		printf("%2x\r\n",*byte);
		return 1;
	}
	else{
        return 0;
	}
}

static bool_t string_to_word(uint16_t* word, char* s)
{
	*word = 0;
	uint8_t t_byte = 0;
	if(!string_to_byte(&t_byte, s)){
	    return 0;
	}
    *word = t_byte;
    *word <<= 8;

    if(!string_to_byte(&t_byte, (s+2))){
	    return 0;
	}
    *word += t_byte;
    *word <<= 8;
    return 1;
}

static uint8_t hex_str_check_sum(char* hex_str)
{
    uint8_t len;
    uint8_t byte;
    uint8_t checksum = 0;
    hex_str++;

    string_to_byte(&len, hex_str);
    len = (len + 4);
    while(len){
        string_to_byte(&byte, hex_str);
        hex_str += 2;
        checksum += byte;
        len--;
    }
    checksum = ~checksum + 1;

    return checksum;
}

//:10010000214601360121470136007EFE09D2190140
//  RECORD_MARK + record_length + load_offset + record_type + data + check_sum
//  :           + 10            + 0100        + 00          + 214601360121470136007EFE09D21901 + 40
#define HEX_LINE_RECORD_MARK		':'
bool_t intelhex_line_analyzing(uint8_t* record_length,
								uint16_t* load_offset,
								uint8_t* record_type,
								uint8_t* data,
                                char* hex_str)
{
    uint8_t i;
    char* hex_line = hex_str;

	if(*hex_line != HEX_LINE_RECORD_MARK){
        return 0;
	}
    hex_line += 1;

    if(!string_to_byte(record_length, hex_line)){
        return 0;
    }
    hex_line += 2;

    if(!string_to_word(load_offset, hex_line)){
        return 0;
    }
    hex_line += 4;

    if(!string_to_byte(record_type, hex_line)){
        return 0;
    }
    hex_line += 2;

    for(i = (*record_length); i > 0; i--){
        if(!string_to_byte(data, hex_line)){
            return 0;
        }
        hex_line += 2;
        data += 1;
    }

    if(!string_to_byte(&i, hex_line)){
        return 0;
    }

    if(hex_str_check_sum(hex_str) != i){
        return 0;
    }

    return 1;
}

char* find_intelhex_len(char* hex_str)
{
    char* hex_line = hex_str;
    while(1){
        if(*hex_line == HEX_LINE_RECORD_MARK){
            break;
        }
        if(*hex_line == '\0'){
            hex_line = 0;
            break;
        }
        hex_line++;
    }
    return hex_line;
}


uint32_t intelhex_bin_data_len(char* hex_str)
{

}

enum {
    RECORD_DATA = 0x00,
    RECORD_END_OF_FILE = 0x01,
    //在 结束记录 中，地址是没有意义的。可以忽略。地址是很典型值为 0000h。
    RECORD_EXTENDED_SEGMENT_ADDRESS = 0x02,
    //当一个 扩展段地址 被读取时，在数据域中的 扩展段地址 被储存，并且运用到后来的记录的读取中。
    //扩展段地址 保持有效，直到被另外一个 扩展段地址 所代替。
    //绝对地址 等于 扩展段地址 乘以16(右移4位) 加上 偏移地址。
    RECORD_START_SEGMENT_ADDRESS = 0x03,
    RECORD_EXTENDED_LINEAR_ADDRESS = 0x04,
    //当一个 扩展线性地址 被读取时，在数据域中的 扩展线性地址 被储存，并且运用到后来的记录的读取中。
    //扩展线性地址 保持有效，直到被另外一个 扩展线性地址 所代替。
    //绝对地址 等于 扩展线性地址 乘以65536(右移16位) 加上 偏移地址。
    RECORD_START_LINEAR_ADDRESS = 0x05
};

bool_t intelhex_to_bin(uint8_t* bin_data, uint16_t* data_len, char* hex_str)
{
    uint8_t record_length;
    uint16_t load_offset;
    uint8_t record_type;
    uint8_t data[256];

    char* hex_line;
    char* hex_str_temp = hex_line;

    uint32_t base_addr = 0;
    uint8_t state = 0;
    while(1){
        hex_line = find_intelhex_len(hex_str_temp);
        hex_str_temp = hex_line+1;

        if(hex_line == 0){
            //not find hex line
            return 0;
        }

        if(!intelhex_line_analyzing(&record_length, &load_offset, &record_type, data, hex_line)){
           //analyzing error
            return 0;
        }

        switch (record_type) {
        case RECORD_DATA:
            memcpy(bin_data+base_addr+load_offset, data, record_length);
            break;
        case RECORD_END_OF_FILE:
            //end
            return 1;
        case RECORD_EXTENDED_SEGMENT_ADDRESS:
            base_addr = 0;
            base_addr += data[0];
            base_addr <<= 8;
            base_addr += data[1];
            base_addr <<= 4;
            break;
        case RECORD_START_SEGMENT_ADDRESS:
//            start_segment_cs = 0;
//            start_segment_cs += data[0];
//            start_segment_cs <<= 8;
//            start_segment_cs += data[1];
//
//            start_segment_ip = 0;
//            start_segment_ip += data[2];
//            start_segment_ip <<= 8;
//            start_segment_ip += data[3];
            break;
        case RECORD_EXTENDED_LINEAR_ADDRESS:
            base_addr = 0;
            base_addr += data[0];
            base_addr <<= 8;
            base_addr += data[1];
            base_addr <<= 16;
            break;
        case RECORD_START_LINEAR_ADDRESS:
//            start_execution = ((uint32_t)get_word(s) << 16) + get_word(s + 4);
//            start_execution = 0;
//            start_execution += data[0];
//            start_execution <<= 8;
//            start_execution += data[1];
//            start_execution <<= 8;
//            start_execution += data[2];
//            start_execution <<= 8;
//            start_execution += data[3];
            break;
        default:
//            printf("Line number %d: record type 0x%02X not supported."
//                   " Must be 0x00 to 0x05\n", line_number, type);
            return 0;
        }
    }
}

char hex_s[] = ":020000040800F2";

int main(void)
{
    uint8_t byte_count;
    uint16_t start_addr;
    uint8_t record_type;
    uint8_t bin_data[256];

    intelhex_line_analyzing(&byte_count, &start_addr, &record_type, bin_data, hex_s);
    printf("%d, %04x, %d", byte_count, start_addr, record_type);
}
